<template>
  <div
    ref="container"
    class="knowledge-graph w-full h-full bg-gradient-to-br from-gray-900 via-gray-800 to-gray-900 rounded-lg overflow-hidden backdrop-blur-sm"
    @click.self="selectedNode = null"
  >
    <div
      v-if="loading"
      class="absolute inset-0 flex items-center justify-center bg-black bg-opacity-50 z-10"
    >
      <div class="text-center">
        <el-icon class="text-6xl text-primary-500 animate-spin mb-4"
          ><Loading
        /></el-icon>
        <div class="text-white text-lg">正在构建知识图谱...</div>
      </div>
    </div>

    <!-- 控制面板 -->
    <div class="absolute top-4 right-4 z-20" @click.stop>
      <div class="flex flex-col items-end">
        <el-tooltip content="重置视角" placement="left">
          <el-button
            circle
            size="large"
            class="mb-4 !w-10 !h-10 flex items-center justify-center"
            @click="resetCamera"
          >
            <el-icon><Refresh /></el-icon>
          </el-button>
        </el-tooltip>
        <el-tooltip content="自动旋转" placement="left">
          <el-button
            circle
            size="large"
            class="mb-4 !w-10 !h-10 flex items-center justify-center"
            :type="autoRotate ? 'primary' : 'default'"
            @click="toggleAutoRotate"
          >
            <el-icon><DCaret /></el-icon>
          </el-button>
        </el-tooltip>
        <el-tooltip content="切换布局" placement="left">
          <el-button
            circle
            size="large"
            class="mb-4 !w-10 !h-10 flex items-center justify-center"
            @click="toggleLayout"
          >
            <el-icon><Grid /></el-icon>
          </el-button>
        </el-tooltip>
        <el-tooltip
          :content="isFullscreen ? '退出全屏' : '全屏显示'"
          placement="left"
        >
          <el-button
            circle
            size="large"
            class="!w-10 !h-10 flex items-center justify-center"
            @click="toggleFullscreen"
          >
            <el-icon><FullScreen /></el-icon>
          </el-button>
        </el-tooltip>
      </div>
    </div>

    <!-- 节点详情 -->
    <div
      v-if="selectedNode"
      class="absolute left-4 bottom-4 z-20 bg-white/95 dark:bg-gray-800/95 rounded-lg shadow-xl p-6 max-w-sm backdrop-blur-sm transform transition-all duration-300 scale-100 opacity-100"
      :class="{ 'scale-95 opacity-0': !selectedNode }"
      @click.stop
    >
      <div class="flex justify-between items-start mb-3">
        <div>
          <h4 class="text-xl font-semibold text-gray-900 dark:text-gray-100">
            {{ selectedNode.name }}
          </h4>
          <div class="flex items-center mt-1">
            <span
              v-if="selectedNode.category === 'spot'"
              class="text-sm text-orange-500 flex items-center"
            >
              <el-icon class="mr-1"><Star /></el-icon>
              {{ selectedNode.rating }}
            </span>
            <span
              v-if="selectedNode.category === 'spot'"
              class="ml-3 text-sm"
              :class="
                selectedNode.price > 0 ? 'text-rose-500' : 'text-emerald-500'
              "
            >
              {{
                selectedNode.price > 0 ? `¥${selectedNode.price}` : "免费开放"
              }}
            </span>
          </div>
        </div>
        <el-button circle size="small" @click="selectedNode = null">
          <el-icon><Close /></el-icon>
        </el-button>
      </div>
      <div class="text-sm text-gray-600 dark:text-gray-400 leading-relaxed">
        {{ getNodeDescription(selectedNode) }}
      </div>
      <div class="mt-4 flex gap-2">
        <el-button
          v-if="selectedNode.category === 'spot'"
          type="primary"
          size="default"
          class="flex-1"
          @click.stop="viewSpotDetail(selectedNode)"
        >
          查看详情
        </el-button>
      </div>
    </div>

    <!-- 图例说明 -->
    <div
      class="absolute left-4 top-4 z-20 bg-white/90 dark:bg-gray-800/90 rounded-lg shadow-lg p-4 backdrop-blur-sm"
      @click.stop
    >
      <div class="text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
        图例说明
      </div>
      <div class="space-y-2">
        <div
          v-for="(color, category) in nodeColors"
          :key="category"
          class="flex items-center gap-2"
        >
          <div
            class="w-3 h-3 rounded-full"
            :style="{ backgroundColor: `#${color.toString(16)}` }"
          ></div>
          <span class="text-xs text-gray-600 dark:text-gray-400">{{
            getCategoryLabel(category)
          }}</span>
        </div>
      </div>
    </div>

    <canvas ref="canvas" class="z-10"></canvas>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount, watch } from "vue";
import * as THREE from "three";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls";
import {
  CSS2DRenderer,
  CSS2DObject,
} from "three/examples/jsm/renderers/CSS2DRenderer";
import { ElIcon, ElButton, ElTooltip } from "element-plus";
import {
  Loading,
  Refresh,
  DCaret,
  Close,
  Star,
  FullScreen,
  Grid,
} from "@element-plus/icons-vue";
import { useRouter } from "vue-router";
import { useFullscreen } from "@vueuse/core";
import gsap from "gsap";
import { ElMessage } from "element-plus";
import { EffectComposer } from "three/examples/jsm/postprocessing/EffectComposer.js";
import { RenderPass } from "three/examples/jsm/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/examples/jsm/postprocessing/UnrealBloomPass.js";
import { ShaderPass } from "three/examples/jsm/postprocessing/ShaderPass.js";
import { FXAAShader } from "three/examples/jsm/shaders/FXAAShader.js";
import { OutlinePass } from "three/examples/jsm/postprocessing/OutlinePass.js";

const router = useRouter();

// 定义props
const props = defineProps({
  nodes: {
    type: Array,
    default: () => [
      {
        id: 1,
        name: "贵港文旅+",
        category: "root",
        description: "贵港市特色旅游资源总览",
      },
      {
        id: 2,
        name: "自然景观",
        category: "category",
        description: "贵港市自然风光景区景点",
      },
      {
        id: 3,
        name: "人文景点",
        category: "category",
        description: "贵港市人文历史景区景点",
      },
      {
        id: 4,
        name: "美食特产",
        category: "category",
        description: "贵港市特色美食与地方特产",
      },
      {
        id: 5,
        name: "民俗文化",
        category: "category",
        description: "贵港市特色民俗文化景点",
      },
      {
        id: 6,
        name: "休闲娱乐",
        category: "category",
        description: "贵港市休闲娱乐场所",
      },
      // 自然景观类景点
      {
        id: 7,
        name: "贵港园博园",
        category: "spot",
        description:
          '占地2000亩的综合性公园，以"一带一路"为主题，融合岭南园林特色',
        rating: 4.6,
        price: 60,
      },
      {
        id: 8,
        name: "江滨公园",
        category: "spot",
        description:
          "位于贵港市区邕江江畔，是市民休闲娱乐的好去处，全长3.5公里",
        rating: 4.5,
        price: 0,
      },
      {
        id: 9,
        name: "观音山公园",
        category: "spot",
        description: "贵港市区标志性山岭公园，山顶设有观景台，可俯瞰全城",
        rating: 4.4,
        price: 0,
      },
      {
        id: 10,
        name: "桂平西山风景区",
        category: "spot",
        description: "国家4A级景区，以奇特的喀斯特地貌和丰富的人文景观著称",
        rating: 4.7,
        price: 45,
      },
      {
        id: 11,
        name: "藤县浔江风景区",
        category: "spot",
        description: "以浔江两岸的自然风光和古镇文化为特色，水域面积广阔",
        rating: 4.5,
        price: 30,
      },
      // 人文景点
      {
        id: 12,
        name: "贵港博物馆",
        category: "spot",
        description: "展示贵港历史文化的综合性博物馆，收藏大量珍贵文物",
        rating: 4.7,
        price: 0,
      },
      {
        id: 13,
        name: "龙母庙",
        category: "spot",
        description: "始建于宋代的著名庙宇，是岭南地区重要的民间信仰场所",
        rating: 4.6,
        price: 0,
      },
      {
        id: 14,
        name: "木乃伊博物馆",
        category: "spot",
        description: "亚洲规模最大的木乃伊博物馆，展示贵港独特的水乡文化",
        rating: 4.5,
        price: 35,
      },
      {
        id: 15,
        name: "贵港旧城",
        category: "spot",
        description: "保存完好的清代古城区，展现传统岭南建筑风貌",
        rating: 4.4,
        price: 0,
      },
      // 民俗文化景点
      {
        id: 16,
        name: "龙母文化园",
        category: "spot",
        description: "展示龙母文化的主题公园，包含龙母祖庙等文化遗产",
        rating: 4.6,
        price: 40,
      },
      {
        id: 17,
        name: "贵港非遗馆",
        category: "spot",
        description: "展示贵港非物质文化遗产，包括龙舟文化、民间工艺等",
        rating: 4.5,
        price: 0,
      },
      // 休闲娱乐景点
      {
        id: 18,
        name: "贵港美食文化街",
        category: "spot",
        description: "汇集贵港特色美食的特色街区，晚上夜市热闹非凡",
        rating: 4.5,
        price: 0,
      },
      {
        id: 19,
        name: "港北万达广场",
        category: "spot",
        description: "贵港最大的现代商业综合体，集购物、餐饮、娱乐于一体",
        rating: 4.4,
        price: 0,
      },
      // 特色美食
      {
        id: 20,
        name: "荷包鸡",
        category: "food",
        description: "贵港名菜，选用本地散养土鸡，配以独特香料制作，肉质鲜嫩",
      },
      {
        id: 21,
        name: "姜糖",
        category: "food",
        description: "贵港传统特产，采用本地老姜制作，甜中带辣，开胃解腻",
      },
      {
        id: 22,
        name: "粉皮",
        category: "food",
        description: "贵港特色小吃，用优质大米制作，口感滑嫩，配料丰富",
      },
      {
        id: 23,
        name: "艾糍粑",
        category: "food",
        description: "传统特色糕点，采用艾草和糯米制作，清香可口",
      },
      {
        id: 24,
        name: "浔江鱼生",
        category: "food",
        description: "贵港特色菜品，选用新鲜浔江鱼制作，配以特制酱料",
      },
      {
        id: 25,
        name: "藤县扣肉",
        category: "food",
        description: "藤县名菜，选用五花肉制作，肥而不腻，入口即化",
      },
      {
        id: 26,
        name: "桂平西山茶",
        category: "food",
        description: "桂平特产，采自西山茶园，清香持久，回甘悠长",
      },
      {
        id: 27,
        name: "贵港粽子",
        category: "food",
        description: "端午节特色美食，用本地糯米制作，馅料丰富",
      },
      {
        id: 28,
        name: "浔江螺蛳",
        category: "food",
        description: "贵港特色小吃，选用浔江新鲜螺蛳，配以特制卤水",
      },
      {
        id: 29,
        name: "贵港酱板鸭",
        category: "food",
        description: "传统特色美食，选用本地麻鸭，经特制酱料腌制",
      },
    ],
  },
  links: {
    type: Array,
    default: () => [
      // 根节点到分类的连接
      { source: 1, target: 2 },
      { source: 1, target: 3 },
      { source: 1, target: 4 },
      { source: 1, target: 5 },
      { source: 1, target: 6 },

      // 自然景观类连接
      { source: 2, target: 7 },
      { source: 2, target: 8 },
      { source: 2, target: 9 },
      { source: 2, target: 10 },
      { source: 2, target: 11 },

      // 人文景点连接
      { source: 3, target: 12 },
      { source: 3, target: 13 },
      { source: 3, target: 14 },
      { source: 3, target: 15 },

      // 民俗文化连接
      { source: 5, target: 16 },
      { source: 5, target: 17 },

      // 休闲娱乐连接
      { source: 6, target: 18 },
      { source: 6, target: 19 },

      // 美食特产连接
      { source: 4, target: 20 },
      { source: 4, target: 21 },
      { source: 4, target: 22 },
      { source: 4, target: 23 },
      { source: 4, target: 24 },
      { source: 4, target: 25 },
      { source: 4, target: 26 },
      { source: 4, target: 27 },
      { source: 4, target: 28 },
      { source: 4, target: 29 },

      // 美食街相关连接
      { source: 18, target: 20 },
      { source: 18, target: 22 },
      { source: 18, target: 24 },
      { source: 18, target: 28 },
    ],
  },
});

const container = ref(null);
const canvas = ref(null);
const loading = ref(true);
const selectedNode = ref(null);
const autoRotate = ref(true);
const isFullscreen = ref(false);
const { toggle: toggleFullscreenState } = useFullscreen(container);

let scene, camera, renderer, labelRenderer, controls, composer;
let nodeObjects = new Map();
let nodeGroups = new Map();
let hoveredNode = null;

// 节点颜色配置
const nodeColors = {
  root: 0xffaa44, // 太阳般的橙黄色
  category: 0x00ffcc, // 调暗的青色
  spot: 0x00aaff, // 明亮的蓝色
  food: 0xffaa00, // 明亮的橙色
};

// 节点大小和亮度配置
const nodeSizes = {
  root: 20, // 太阳大小
  category: 10, // 行星大小
  spot: 8, // 小行星大小
  food: 6, // 卫星大小
};

// 修改节点亮度配置
const nodeIntensities = {
  root: 2.0, // 太阳亮度
  category: 0.6, // 降低分类节点亮度（原来是0.8）
  spot: 0.5, // 小行星亮度
  food: 0.3, // 卫星亮度
};

// 获取分类标签
const getCategoryLabel = (category) => {
  const labels = {
    root: "中心节点",
    category: "分类节点",
    spot: "景点节点",
    food: "美食节点",
  };
  return labels[category] || category;
};

// 切换全屏
const toggleFullscreen = async () => {
  try {
    if (!document.fullscreenElement) {
      await container.value.requestFullscreen();
      isFullscreen.value = true;
    } else {
      await document.exitFullscreen();
      isFullscreen.value = false;
    }

    setTimeout(() => {
      handleResize();
      resetCamera();
    }, 100);
  } catch (err) {
    console.error("全屏切换失败:", err);
    ElMessage.error("全屏切换失败");
  }
};

// 监听全屏变化
watch(isFullscreen, (newVal) => {
  if (newVal) {
    handleResize();
  } else {
    setTimeout(handleResize, 100);
  }
});

// 修改力导向图相关变量
let simulation = null;
const nodePositions = new Map();
const nodeVelocities = new Map();
const repulsionForce = 150; // 减小斥力
const springForce = 0.03; // 减小弹力
const damping = 0.8; // 增加阻尼
const minDistance = 50; // 最小节点距离
const maxForce = 10; // 最大力限制
const stabilityThreshold = 0.1; // 稳定性阈值
let isStable = false; // 是否稳定的标志
let stableCount = 0; // 稳定计数器

// 力导向图布局更新函数
const updateForceLayout = () => {
  if (!nodeGroups || nodeGroups.size === 0 || isStable) return;

  let totalMovement = 0;

  // 计算节点间的斥力
  nodeGroups.forEach((group1, id1) => {
    let fx = 0,
      fy = 0,
      fz = 0;
    const node1 = props.nodes.find((n) => n.id === id1);

    nodeGroups.forEach((group2, id2) => {
      if (id1 !== id2) {
        const dx = group1.position.x - group2.position.x;
        const dy = group1.position.y - group2.position.y;
        const dz = group1.position.z - group2.position.z;
        const distance = Math.sqrt(dx * dx + dy * dy + dz * dz);

        if (distance < minDistance) {
          const force = Math.min(
            repulsionForce / (distance * distance),
            maxForce,
          );
          fx += dx * force;
          fy += dy * force;
          fz += dz * force;
        }
      }
    });

    // 根据节点类型调整力的大小
    const typeMultiplier =
      node1.category === "root" ? 0.1 : node1.category === "category" ? 0.5 : 1;

    // 更新速度和位置
    const velocity = nodeVelocities.get(id1) || { x: 0, y: 0, z: 0 };
    velocity.x = (velocity.x + fx * typeMultiplier) * damping;
    velocity.y = (velocity.y + fy * typeMultiplier) * damping;
    velocity.z = (velocity.z + fz * typeMultiplier) * damping;

    // 限制最大速度
    const speed = Math.sqrt(
      velocity.x * velocity.x +
        velocity.y * velocity.y +
        velocity.z * velocity.z,
    );
    if (speed > maxForce) {
      const scale = maxForce / speed;
      velocity.x *= scale;
      velocity.y *= scale;
      velocity.z *= scale;
    }

    nodeVelocities.set(id1, velocity);

    // 应用弹簧力将节点拉回原始位置
    const originalPos = nodePositions.get(id1);
    if (originalPos) {
      const dx = originalPos.x - group1.position.x;
      const dy = originalPos.y - group1.position.y;
      const dz = originalPos.z - group1.position.z;
      velocity.x += dx * springForce * typeMultiplier;
      velocity.y += dy * springForce * typeMultiplier;
      velocity.z += dz * springForce * typeMultiplier;
    }

    // 更新位置
    const oldPos = group1.position.clone();
    group1.position.x += velocity.x;
    group1.position.y += velocity.y;
    group1.position.z += velocity.z;

    // 计算移动距离
    const movement = oldPos.distanceTo(group1.position);
    totalMovement += movement;
  });

  // 检查是否达到稳定状态
  const averageMovement = totalMovement / nodeGroups.size;
  if (averageMovement < stabilityThreshold) {
    stableCount++;
    if (stableCount > 30) {
      // 连续30帧稳定才真正稳定
      isStable = true;
      console.log("Graph stabilized");
    }
  } else {
    stableCount = 0;
  }
};

// 修改切换布局函数
const toggleLayout = () => {
  if (!nodeGroups) return;

  // 重置稳定状态
  isStable = false;
  stableCount = 0;

  props.nodes.forEach((node) => {
    const group = nodeGroups.get(node.id);
    if (group) {
      const angle = Math.random() * Math.PI * 2;
      const radius =
        node.category === "root"
          ? 0
          : node.category === "category"
            ? 100 + Math.random() * 50
            : 150 + Math.random() * 100;

      const newPosition = new THREE.Vector3(
        Math.cos(angle) * radius,
        (Math.random() - 0.5) * (node.category === "root" ? 20 : 100),
        Math.sin(angle) * radius,
      );

      // 更新初始位置
      nodePositions.set(node.id, newPosition.clone());
      // 重置速度
      nodeVelocities.set(node.id, { x: 0, y: 0, z: 0 });

      // 使用动画过渡到新位置
      gsap.to(group.position, {
        x: newPosition.x,
        y: newPosition.y,
        z: newPosition.z,
        duration: 1,
        ease: "power2.inOut",
        onUpdate: () => {
          controls.update();
        },
      });
    }
  });
};

// 修改节点材质创建函数
const createNodeMaterial = (category) => {
  const color = nodeColors[category];
  const size = nodeSizes[category];
  const intensity = nodeIntensities[category];

  // 创建主体球体材质
  const sphereMaterial = new THREE.MeshPhysicalMaterial({
    color: color,
    metalness: category === "root" ? 0.3 : category === "category" ? 0.8 : 0.7, // 增加分类节点的金属度
    roughness: category === "root" ? 0.2 : category === "category" ? 0.6 : 0.4, // 增加分类节点的粗糙度
    transparent: true,
    opacity: 0.9,
    envMapIntensity: intensity,
    clearcoat: category === "root" ? 1.0 : category === "category" ? 0.3 : 0.5, // 降低分类节点的清漆效果
    clearcoatRoughness:
      category === "root" ? 0.1 : category === "category" ? 0.5 : 0.3, // 增加分类节点的清漆粗糙度
    emissive: color,
    emissiveIntensity: intensity,
  });

  // 创建发光球体材质
  const glowMaterial = new THREE.ShaderMaterial({
    uniforms: {
      glowColor: { value: new THREE.Color(color) },
      viewVector: { value: new THREE.Vector3() },
      c: {
        value: category === "root" ? 0.1 : category === "category" ? 0.3 : 0.2,
      }, // 调整分类节点的发光衰减
      p: {
        value: category === "root" ? 1.4 : category === "category" ? 2.5 : 2.0,
      }, // 调整分类节点的发光强度
      intensity: { value: intensity },
    },
    vertexShader: `
      uniform vec3 viewVector;
      varying float intensity;
      void main() {
        vec3 vNormal = normalize(normalMatrix * normal);
        vec3 vNormel = normalize(normalMatrix * viewVector);
        intensity = pow(0.6 - dot(vNormal, vNormel), 2.0);
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    `,
    fragmentShader: `
      uniform vec3 glowColor;
      uniform float intensity;
      varying float intensity;
      void main() {
        vec3 glow = glowColor * intensity * 2.0;
        gl_FragColor = vec4(glow, intensity);
      }
    `,
    side: THREE.BackSide,
    blending: THREE.AdditiveBlending,
    transparent: true,
    depthWrite: false,
  });

  return { sphereMaterial, glowMaterial };
};

// 创建流动线条材质
const createFlowLineMaterial = () => {
  return new THREE.ShaderMaterial({
    uniforms: {
      time: { value: 0 },
      color: { value: new THREE.Color(0x4a9eff) },
      opacity: { value: 0.8 },
      dashScale: { value: 0.25 },
      dashOffset: { value: 0 },
      glowColor: { value: new THREE.Color(0x00ffff) },
      glowIntensity: { value: 0.8 },
    },
    vertexShader: `
      uniform float time;
      varying vec2 vUv;
      void main() {
        vUv = uv;
        gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
    `,
    fragmentShader: `
      uniform vec3 color;
      uniform vec3 glowColor;
      uniform float time;
      uniform float opacity;
      uniform float dashScale;
      uniform float dashOffset;
      uniform float glowIntensity;
      varying vec2 vUv;
      
      void main() {
        float flow1 = fract(vUv.x * dashScale - time * 0.8);
        float flow2 = fract(vUv.x * dashScale * 1.5 - time * 0.5);
        float dash = smoothstep(0.0, 0.1, flow1) * smoothstep(1.0, 0.9, flow1) +
                    smoothstep(0.0, 0.1, flow2) * smoothstep(1.0, 0.9, flow2);
        
        float glow = smoothstep(0.8, 0.0, abs(vUv.x * 2.0 - 1.0)) * 1.5;
        
        float pulse = sin(time * 3.0) * 0.1 + 0.9;
        
        vec3 finalColor = mix(color, glowColor, glow * glowIntensity * pulse);
        float finalOpacity = opacity * (0.4 + dash * 0.6 + glow * 0.4) * pulse;
        
        gl_FragColor = vec4(finalColor, finalOpacity);
      }
    `,
    transparent: true,
    blending: THREE.AdditiveBlending,
    depthWrite: false,
    depthTest: true,
  });
};

// 修改初始化场景函数
const initScene = () => {
  scene = new THREE.Scene();
  scene.background = new THREE.Color(0x1a1a1a);

  // 调整相机参数以适应横向视图
  camera = new THREE.PerspectiveCamera(
    45,
    container.value.clientWidth / container.value.clientHeight,
    0.1,
    1000,
  );
  camera.position.set(0, 100, 300);
  camera.lookAt(0, 0, 0);

  // 设置渲染器
  renderer = new THREE.WebGLRenderer({
    canvas: canvas.value,
    antialias: true,
    alpha: true,
  });
  renderer.setSize(container.value.clientWidth, container.value.clientHeight);
  renderer.setPixelRatio(window.devicePixelRatio);

  // 设置标签渲染器
  labelRenderer = new CSS2DRenderer();
  labelRenderer.setSize(
    container.value.clientWidth,
    container.value.clientHeight,
  );
  labelRenderer.domElement.style.position = "absolute";
  labelRenderer.domElement.style.top = "0";
  labelRenderer.domElement.style.pointerEvents = "none";
  container.value.appendChild(labelRenderer.domElement);

  // 设置控制器
  controls = new OrbitControls(camera, renderer.domElement);
  controls.enableDamping = true;
  controls.dampingFactor = 0.05;
  controls.maxPolarAngle = Math.PI;
  controls.minPolarAngle = 0;
  controls.maxAzimuthAngle = Infinity;
  controls.minAzimuthAngle = -Infinity;
  controls.enableZoom = true;
  controls.zoomSpeed = 0.5;
  controls.enablePan = false;
  controls.rotateSpeed = 0.5;
  controls.autoRotate = true;
  controls.autoRotateSpeed = 1.0;
  controls.target.set(0, 0, 0);
  controls.update();

  // 修改光照
  const ambientLight = new THREE.AmbientLight(0xffffff, 0.2); // 降低环境光
  scene.add(ambientLight);

  const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8); // 增强直射光
  directionalLight.position.set(1, 1, 1);
  scene.add(directionalLight);

  const pointLight = new THREE.PointLight(0xffa726, 0.8); // 增加一个橙色点光源
  pointLight.position.set(0, 0, 0); // 放在中心位置
  scene.add(pointLight);

  // 修改后期处理效果
  composer = new EffectComposer(renderer);

  const renderPass = new RenderPass(scene, camera);
  composer.addPass(renderPass);

  const bloomPass = new UnrealBloomPass(
    new THREE.Vector2(
      container.value.clientWidth,
      container.value.clientHeight,
    ),
    1.5, // 略微降低泛光强度
    0.8, // 增加半径
    0.5, // 调整阈值
  );
  composer.addPass(bloomPass);

  // 添加FXAA抗锯齿
  const fxaaPass = new ShaderPass(FXAAShader);
  const pixelRatio = renderer.getPixelRatio();
  fxaaPass.material.uniforms["resolution"].value.x =
    1 / (container.value.clientWidth * pixelRatio);
  fxaaPass.material.uniforms["resolution"].value.y =
    1 / (container.value.clientHeight * pixelRatio);
  composer.addPass(fxaaPass);

  createGraph();
  setupInteraction();
};

// 修改创建连线部分
const createGraph = () => {
  // 创建节点
  props.nodes.forEach((node) => {
    const group = new THREE.Group();

    // 获取节点大小
    const size = nodeSizes[node.category];

    // 创建主体球体
    const sphereGeometry = new THREE.SphereGeometry(size, 32, 32);
    const { sphereMaterial, glowMaterial } = createNodeMaterial(node.category);

    // 创建主体球体
    const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.userData = { ...node, type: "node" };

    // 创建发光球体（略大于主体球体）
    const glowGeometry = new THREE.SphereGeometry(
      size * (node.category === "root" ? 1.5 : 1.2),
      32,
      32,
    );
    const glowSphere = new THREE.Mesh(glowGeometry, glowMaterial);

    // 添加环绕光环
    const ringGeometry = new THREE.TorusGeometry(
      size * (node.category === "root" ? 1.8 : 1.4),
      node.category === "root" ? 0.5 : 0.3,
      16,
      100,
    );
    const ringMaterial = new THREE.MeshPhongMaterial({
      color: nodeColors[node.category],
      transparent: true,
      opacity: node.category === "root" ? 0.4 : 0.3,
      side: THREE.DoubleSide,
      blending: THREE.AdditiveBlending,
      emissive: nodeColors[node.category],
      emissiveIntensity: nodeIntensities[node.category],
    });
    const ring = new THREE.Mesh(ringGeometry, ringMaterial);
    ring.rotation.x = Math.PI / 2;

    // 创建标签
    const labelDiv = document.createElement("div");
    labelDiv.className =
      "text-xs font-medium px-2 py-1 bg-gray-800/90 rounded shadow-md transform transition-all duration-200";
    labelDiv.innerHTML = `
      <div class="whitespace-nowrap text-gray-100">
        ${node.name}
        ${node.rating ? `<span class="text-orange-500 ml-1">${node.rating}分</span>` : ""}
      </div>
    `;
    const label = new CSS2DObject(labelDiv);
    label.position.set(0, size * 1.5, 0);

    // 将所有元素添加到组中
    group.add(sphere);
    group.add(glowSphere);
    group.add(ring);
    group.add(label);

    // 记录初始位置
    const angle = Math.random() * Math.PI * 2;
    const radius = node.category === "root" ? 0 : 150 + Math.random() * 100;
    const position = new THREE.Vector3(
      Math.cos(angle) * radius,
      (Math.random() - 0.5) * 100,
      Math.sin(angle) * radius,
    );

    group.position.copy(position);
    nodePositions.set(node.id, position.clone());
    nodeVelocities.set(node.id, { x: 0, y: 0, z: 0 });

    scene.add(group);
    nodeObjects.set(node.id, sphere);
    nodeGroups.set(node.id, group);
  });

  // 使用新的流动线条材质
  props.links.forEach((link) => {
    const sourceNode = nodeObjects.get(link.source);
    const targetNode = nodeObjects.get(link.target);

    // 创建曲线路径
    const curve = new THREE.QuadraticBezierCurve3(
      sourceNode.position,
      new THREE.Vector3(
        (sourceNode.position.x + targetNode.position.x) / 2,
        (sourceNode.position.y + targetNode.position.y) / 2 + 20,
        (sourceNode.position.z + targetNode.position.z) / 2,
      ),
      targetNode.position,
    );

    const points = curve.getPoints(50);
    const geometry = new THREE.BufferGeometry().setFromPoints(points);

    // 使用新的流动线条材质
    const material = createFlowLineMaterial();
    const line = new THREE.Line(geometry, material);
    scene.add(line);

    // 更新连线位置
    const updateLine = () => {
      if (!scene || !nodeGroups) return;

      const sourceGroup = nodeGroups.get(link.source);
      const targetGroup = nodeGroups.get(link.target);

      if (!sourceGroup || !targetGroup) return;

      const midPoint = new THREE.Vector3(
        (sourceGroup.position.x + targetGroup.position.x) / 2,
        (sourceGroup.position.y + targetGroup.position.y) / 2 + 20,
        (sourceGroup.position.z + targetGroup.position.z) / 2,
      );

      const curve = new THREE.QuadraticBezierCurve3(
        sourceGroup.position,
        midPoint,
        targetGroup.position,
      );

      const points = curve.getPoints(50);
      geometry.setFromPoints(points);
    };

    // 初始更新
    updateLine();

    // 添加到动画循环
    const animate = () => {
      if (!scene) return;
      updateLine();
      requestAnimationFrame(animate);
    };
    animate();
  });

  loading.value = false;
};

// 设置交互
const setupInteraction = () => {
  const raycaster = new THREE.Raycaster();
  raycaster.params.Points.threshold = 0.1;
  const mouse = new THREE.Vector2();

  // 鼠标移动事件
  const onMouseMove = (event) => {
    event.preventDefault();
    event.stopPropagation();

    const rect = container.value.getBoundingClientRect();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(
      Array.from(nodeObjects.values()),
      true,
    );

    let foundNode = false;
    for (const intersect of intersects) {
      const userData = intersect.object.userData;
      if (userData && userData.type === "node") {
        foundNode = true;
        if (hoveredNode !== intersect.object) {
          if (hoveredNode) {
            const prevGroup = nodeGroups.get(hoveredNode.userData.id);
            if (prevGroup) {
              gsap.to(prevGroup.scale, {
                x: 1,
                y: 1,
                z: 1,
                duration: 0.3,
              });
            }
          }

          hoveredNode = intersect.object;
          const newGroup = nodeGroups.get(hoveredNode.userData.id);
          if (newGroup) {
            gsap.to(newGroup.scale, {
              x: 1.2,
              y: 1.2,
              z: 1.2,
              duration: 0.3,
            });
          }
        }
        container.value.style.cursor = "pointer";
        break;
      }
    }

    if (!foundNode && hoveredNode) {
      const group = nodeGroups.get(hoveredNode.userData.id);
      if (group) {
        gsap.to(group.scale, {
          x: 1,
          y: 1,
          z: 1,
          duration: 0.3,
        });
      }
      hoveredNode = null;
      container.value.style.cursor = "default";
    }
  };

  // 点击事件
  const onClick = (event) => {
    event.preventDefault();
    event.stopPropagation();

    const rect = container.value.getBoundingClientRect();
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1;
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1;

    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects(
      Array.from(nodeObjects.values()),
      true,
    );

    let clickedNode = null;
    for (const intersect of intersects) {
      const userData = intersect.object.userData;
      if (userData && userData.type === "node") {
        clickedNode = userData;
        break;
      }
    }

    if (clickedNode) {
      selectedNode.value = clickedNode;
      // 聚焦到选中的节点
      const group = nodeGroups.get(clickedNode.id);
      if (group) {
        gsap.to(camera.position, {
          x: group.position.x + 100,
          y: group.position.y + 50,
          z: group.position.z + 100,
          duration: 1,
          ease: "power2.inOut",
          onUpdate: () => {
            camera.lookAt(group.position);
            controls.target.copy(group.position);
            controls.update();
          },
        });
      }
    }
  };

  // 移除旧的事件监听器
  container.value.removeEventListener("mousemove", onMouseMove);
  container.value.removeEventListener("click", onClick);

  // 添加新的事件监听器
  container.value.addEventListener("mousemove", onMouseMove);
  container.value.addEventListener("click", onClick);

  // 返回清理函数
  return () => {
    container.value?.removeEventListener("mousemove", onMouseMove);
    container.value?.removeEventListener("click", onClick);
  };
};

// 获取节点描述
const getNodeDescription = (node) => {
  if (!node) return "";
  let description = node.description || "";
  if (node.category === "spot") {
    description += `\n${node.price > 0 ? `票价：¥${node.price}` : "免费开放"}`;
  }
  return description;
};

// 查看景点详情
const viewSpotDetail = (node) => {
  if (!node || node.category !== "spot") return;
  try {
    // 停止所有动画循环
    scene = null;
    selectedNode.value = null;
    router.push(`/spots/${node.id}`);
  } catch (err) {
    console.error("跳转失败:", err);
    ElMessage.error("页面跳转失败");
  }
};

// 重置相机位置
const resetCamera = () => {
  if (!camera || !controls) return;

  gsap.to(camera.position, {
    x: 0,
    y: 50,
    z: 300,
    duration: 1,
    ease: "power2.inOut",
    onUpdate: () => {
      camera.lookAt(new THREE.Vector3(0, 0, 0));
      controls.target.set(0, 0, 0);
      controls.update();
    },
  });
};

// 切换自动旋转
const toggleAutoRotate = () => {
  if (!controls) return;
  autoRotate.value = !autoRotate.value;
  controls.autoRotate = autoRotate.value;
  controls.autoRotateSpeed = 1.0;

  if (autoRotate.value) {
    gsap.to(camera.position, {
      y: 100,
      duration: 1,
      ease: "power2.inOut",
      onUpdate: () => {
        controls.update();
      },
    });
  }
};

// 动画循环
const animate = () => {
  if (!scene || !camera || !controls) return;

  requestAnimationFrame(animate);
  controls.update();

  // 更新力导向图布局
  updateForceLayout();

  // 更新发光效果
  const time = performance.now() * 0.001;
  scene.traverse((object) => {
    if (
      object instanceof THREE.Line &&
      object.material instanceof THREE.ShaderMaterial
    ) {
      object.material.uniforms.time.value = time;
      object.material.uniforms.dashOffset.value = time * 0.8;
      object.material.uniforms.glowIntensity.value =
        0.8 + Math.sin(time * 3) * 0.3;
    }
    // 更新节点发光效果
    if (
      object instanceof THREE.Mesh &&
      object.material.type === "ShaderMaterial" &&
      object.material.uniforms.viewVector
    ) {
      object.material.uniforms.viewVector.value =
        new THREE.Vector3().subVectors(camera.position, object.position);
    }
  });

  if (composer && labelRenderer) {
    composer.render();
    labelRenderer.render(scene, camera);
  }
};

// 窗口大小变化处理
const handleResize = () => {
  if (!container.value) return;

  const width = container.value.clientWidth;
  const height = container.value.clientHeight;

  camera.aspect = width / height;
  camera.updateProjectionMatrix();

  renderer.setSize(width, height);
  composer.setSize(width, height);
  labelRenderer.setSize(width, height);

  // 更新FXAA分辨率
  const fxaaPass = composer.passes.find(
    (pass) => pass.material && pass.material.uniforms.resolution,
  );
  if (fxaaPass) {
    fxaaPass.material.uniforms.resolution.value.x =
      1 / (width * window.devicePixelRatio);
    fxaaPass.material.uniforms.resolution.value.y =
      1 / (height * window.devicePixelRatio);
  }
};

// 在组件挂载时添加暗色模式监听
onMounted(() => {
  initScene();
  animate();
  window.addEventListener("resize", handleResize);

  // 清理函数
  onBeforeUnmount(() => {
    window.removeEventListener("resize", handleResize);

    // 清理Three.js资源
    if (scene) {
      scene.traverse((object) => {
        if (object.geometry) {
          object.geometry.dispose();
        }
        if (object.material) {
          if (Array.isArray(object.material)) {
            object.material.forEach((material) => material.dispose());
          } else {
            object.material.dispose();
          }
        }
      });
    }

    // 清理渲染器
    if (renderer) {
      renderer.dispose();
      renderer.forceContextLoss();
      renderer.domElement = null;
    }

    // 清理后期处理
    if (composer) {
      composer.passes.forEach((pass) => {
        if (pass.dispose) {
          pass.dispose();
        }
      });
    }

    // 清理标签渲染器
    if (labelRenderer) {
      labelRenderer.domElement.remove();
    }

    // 清理控制器
    if (controls) {
      controls.dispose();
    }

    // 重置变量
    scene = null;
    camera = null;
    renderer = null;
    labelRenderer = null;
    controls = null;
    composer = null;
    nodeObjects.clear();
    nodeGroups.clear();
    hoveredNode = null;
  });
});
</script>

<style lang="scss" scoped>
.knowledge-graph {
  position: relative;

  canvas {
    width: 100%;
    height: 100%;
  }

  :deep(.el-button) {
    @apply bg-white/90 dark:bg-gray-800/90 border-gray-200 dark:border-gray-700 transition-all duration-200;
    margin: 0 !important;
    padding: 0 !important;

    &:hover {
      @apply bg-white dark:bg-gray-700 transform scale-105 shadow-lg;
    }

    &.is-active {
      @apply bg-primary-500 text-white border-primary-500;

      &:hover {
        @apply bg-primary-600;
      }
    }

    &.el-button--primary {
      @apply bg-primary-500 hover:bg-primary-600 border-primary-500 
             dark:bg-primary-600 dark:hover:bg-primary-700 dark:border-primary-600;
    }
  }

  .node-label {
    @apply transition-all duration-200 transform;

    &:hover {
      @apply scale-110;
    }
  }
}
</style>
